#ifndef __S_3D_VERTEX_H_INCLUDED__
#define __S_3D_VERTEX_H_INCLUDED__
#include "SColorf.h"
#include "LDXConfig.h"

namespace ldx
{
	namespace render
	{
		enum E_INDEX_TYPE
		{
			EIT_16BIT = 0,
			EIT_32BIT
		};

		//! Enumeration for all vertex types there are.
		enum E_VERTEX_TYPE
		{
			//! Standard vertex type used by the Irrlicht engine, video::S3DVertex.
			EVT_STANDARD = 0,

			//! Vertex with two texture coordinates, video::S3DVertex2TCoords.
			/** Usually used for geometry with lightmaps or other special materials. */
			EVT_2TCOORDS,

			//! Vertex with a tangent and binormal vector, video::S3DVertexTangents.
			/** Usually used for tangent space normal mapping. */
			EVT_TANGENTS
		};

		//! Array holding the built in vertex type names
		const char* const sBuiltInVertexTypeNames[] =
		{
			"standard",
			"2tcoords",
			"tangents",
			0
		};

		//! standard vertex used by the Irrlicht engine.
		struct S3DVertex
		{
			//! default constructor
			S3DVertex()/*:Color(0,0,0,0) */{}

			//! constructor
			S3DVertex(float x, float y, float z, float nx, float ny, float nz, /*SColorf c,*/ float tu, float tv)
				: Pos(x, y, z), Normal(nx, ny, nz),/* Color(c),*/ TCoords(tu, tv) {}

			//! constructor
			S3DVertex(const mt::vec3f& pos, const mt::vec3f& normal,
				/*SColorf color,*/ const mt::vector2d<float>& tcoords)
				: Pos(pos), Normal(normal), /*Color(color),*/ TCoords(tcoords) {}

			//! Position
			mt::vec3f Pos;

			//! Normal vector
			mt::vec3f Normal;

			//! Color
			//SColorf Color;

			//! Texture coordinates
			mt::vector2d<float> TCoords;

			bool operator==(const S3DVertex& other) const
			{
				return ((Pos == other.Pos) && (Normal == other.Normal) &&
					/*(Color == other.Color) &&*/ (TCoords == other.TCoords));
			}

			bool operator!=(const S3DVertex& other) const
			{
				return ((Pos != other.Pos) || (Normal != other.Normal) ||
					/*(Color != other.Color) ||*/ (TCoords != other.TCoords));
			}

			bool operator<(const S3DVertex& other) const
			{
				return ((Pos < other.Pos) ||
					((Pos == other.Pos) && (Normal < other.Normal)) ||
					((Pos == other.Pos) && (Normal == other.Normal)/* && (Color < other.Color)*/) ||
					((Pos == other.Pos) && (Normal == other.Normal) /*&& (Color == other.Color)*/ && (TCoords < other.TCoords)));
			}

			E_VERTEX_TYPE getType() const
			{
				return EVT_STANDARD;
			}

			S3DVertex getInterpolated(const S3DVertex& other, float d)
			{
				d = mt::clamp(d, 0.0f, 1.0f);
				return S3DVertex(Pos.getInterpolated(other.Pos, d),
					Normal.getInterpolated(other.Normal, d),/*
					Color.getInterpolated(other.Color, d),*/
					TCoords.getInterpolated(other.TCoords, d));
			}
		};


		//! Vertex with two texture coordinates.
		/** Usually used for geometry with lightmaps
		or other special materials.
		*/
		struct S3DVertex2TCoords : public S3DVertex
		{
			//! default constructor
			S3DVertex2TCoords() : S3DVertex() {}

			//! constructor with two different texture coords, but no normal
			S3DVertex2TCoords(float x, float y, float z, /*SColorf c,*/ float tu, float tv, float tu2, float tv2)
				: S3DVertex(x, y, z, 0.0f, 0.0f, 0.0f, /*c,*/ tu, tv), TCoords2(tu2, tv2) {}

			//! constructor with two different texture coords, but no normal
			S3DVertex2TCoords(const mt::vec3f& pos, /*SColorf color,*/
				const mt::vector2d<float>& tcoords, const mt::vector2d<float>& tcoords2)
				: S3DVertex(pos, mt::vec3f(),/* color,*/ tcoords), TCoords2(tcoords2) {}

			//! constructor with all values
			S3DVertex2TCoords(const mt::vec3f& pos, const mt::vec3f& normal, /*const SColorf& color,*/
				const mt::vector2d<float>& tcoords, const mt::vector2d<float>& tcoords2)
				: S3DVertex(pos, normal, /*color,*/ tcoords), TCoords2(tcoords2) {}

			//! constructor with all values
			S3DVertex2TCoords(float x, float y, float z, float nx, float ny, float nz, /*SColorf c,*/ float tu, float tv, float tu2, float tv2)
				: S3DVertex(x, y, z, nx, ny, nz, /*c, */tu, tv), TCoords2(tu2, tv2) {}

			//! constructor with the same texture coords and normal
			S3DVertex2TCoords(float x, float y, float z, float nx, float ny, float nz,/* SColorf c, */float tu, float tv)
				: S3DVertex(x, y, z, nx, ny, nz,/* c, */tu, tv), TCoords2(tu, tv) {}

			//! constructor with the same texture coords and normal
			S3DVertex2TCoords(const mt::vec3f& pos, const mt::vec3f& normal,
				/*SColorf color,*/ const mt::vector2d<float>& tcoords)
				: S3DVertex(pos, normal,/* color,*/ tcoords), TCoords2(tcoords) {}

			//! constructor from S3DVertex
			S3DVertex2TCoords(S3DVertex& o) : S3DVertex(o) {}

			//! Second set of texture coordinates
			mt::vector2d<float> TCoords2;

			//! Equality operator
			bool operator==(const S3DVertex2TCoords& other) const
			{
				return ((static_cast<S3DVertex>(*this) == other) &&
					(TCoords2 == other.TCoords2));
			}

			//! Inequality operator
			bool operator!=(const S3DVertex2TCoords& other) const
			{
				return ((static_cast<S3DVertex>(*this) != other) ||
					(TCoords2 != other.TCoords2));
			}

			bool operator<(const S3DVertex2TCoords& other) const
			{
				return ((static_cast<S3DVertex>(*this) < other) ||
					((static_cast<S3DVertex>(*this) == other) && (TCoords2 < other.TCoords2)));
			}

			E_VERTEX_TYPE getType() const
			{
				return EVT_2TCOORDS;
			}

			S3DVertex2TCoords getInterpolated(const S3DVertex2TCoords& other, float d)
			{
				d = mt::clamp(d, 0.0f, 1.0f);
				return S3DVertex2TCoords(Pos.getInterpolated(other.Pos, d),
					Normal.getInterpolated(other.Normal, d),
				/*	Color.getInterpolated(other.Color, d),*/
					TCoords.getInterpolated(other.TCoords, d),
					TCoords2.getInterpolated(other.TCoords2, d));
			}
		};


		//! Vertex with a tangent and binormal vector.
		/** Usually used for tangent space normal mapping. */
		struct S3DVertexTangents : public S3DVertex
		{
			//! default constructor
			S3DVertexTangents() : S3DVertex() { }

			//! constructor
			S3DVertexTangents(float x, float y, float z, float nx = 0.0f, float ny = 0.0f, float nz = 0.0f,
				SColorf c = 0xFFFFFFFF, float tu = 0.0f, float tv = 0.0f,
				float tanx = 0.0f, float tany = 0.0f, float tanz = 0.0f,
				float bx = 0.0f, float by = 0.0f, float bz = 0.0f)
				: S3DVertex(x, y, z, nx, ny, nz, /*c,*/ tu, tv), Tangent(tanx, tany, tanz), Binormal(bx, by, bz) { }

			//! constructor
			S3DVertexTangents(const mt::vec3f& pos, /*SColorf c,*/
				const mt::vector2df& tcoords)
				: S3DVertex(pos, mt::vec3f(), /*c,*/ tcoords) { }

			//! constructor
			S3DVertexTangents(const mt::vec3f& pos,
				const mt::vec3f& normal, /*SColorf c,*/
				const mt::vector2df& tcoords,
				const mt::vec3f& tangent = mt::vec3f(),
				const mt::vec3f& binormal = mt::vec3f())
				: S3DVertex(pos, normal,/* c,*/ tcoords), Tangent(tangent), Binormal(binormal) { }

			//! Tangent vector along the x-axis of the texture
			mt::vec3f Tangent;

			//! Binormal vector (tangent x normal)
			mt::vec3f Binormal;

			bool operator==(const S3DVertexTangents& other) const
			{
				return ((static_cast<S3DVertex>(*this) == other) &&
					(Tangent == other.Tangent) &&
					(Binormal == other.Binormal));
			}

			bool operator!=(const S3DVertexTangents& other) const
			{
				return ((static_cast<S3DVertex>(*this) != other) ||
					(Tangent != other.Tangent) ||
					(Binormal != other.Binormal));
			}

			bool operator<(const S3DVertexTangents& other) const
			{
				return ((static_cast<S3DVertex>(*this) < other) ||
					((static_cast<S3DVertex>(*this) == other) && (Tangent < other.Tangent)) ||
					((static_cast<S3DVertex>(*this) == other) && (Tangent == other.Tangent) && (Binormal < other.Binormal)));
			}

			E_VERTEX_TYPE getType() const
			{
				return EVT_TANGENTS;
			}

			S3DVertexTangents getInterpolated(const S3DVertexTangents& other, float d)
			{
				d = mt::clamp(d, 0.0f, 1.0f);
				return S3DVertexTangents(Pos.getInterpolated(other.Pos, d),
					Normal.getInterpolated(other.Normal, d),
					/*Color.getInterpolated(other.Color, d),*/
					TCoords.getInterpolated(other.TCoords, d),
					Tangent.getInterpolated(other.Tangent, d),
					Binormal.getInterpolated(other.Binormal, d));
			}
		};



		inline mt::uint getVertexPitchFromType(E_VERTEX_TYPE vertexType)
		{
			switch (vertexType)
			{
			case render::EVT_2TCOORDS:
				return sizeof(render::S3DVertex2TCoords);
			case render::EVT_TANGENTS:
				return sizeof(render::S3DVertexTangents);
			default:
				return sizeof(render::S3DVertex);
			}
		}


	} // end namespace video
} // end namespace irr

#endif

